#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#define TRUE    1
#define FALSE   0

#define MAX_TIMERS      16          /* number of timers */
typedef unsigned int    TIME;       /* how time is actually strored */
#define VERY_LONG_TIME  0xFFFFFFFF  /* longest time possible */
typedef void (*EVENT)(void);        /* event function pointer */

struct timer {
    int     inuse;                  /* TRUE if in use */
    TIME    time;                   /* relative time to wait */
    EVENT   event;                  /* set to TRUE at timeout */
} timers[MAX_TIMERS];               /* set of timers */

sigset_t save_mask;
void signal_alarm(int signo);
void disable_interrupts();
void enable_interrupts();
void start_physical_timer(TIME time);

void timers_init() {
    struct timer *t;

    for (t = timers; t < &timers[MAX_TIMERS]; t++) {
        t->inuse = FALSE;
    }

    signal(SIGALRM, signal_alarm);
}

struct timer *timer_next = NULL;    /* timer we expect to run down next */
TIME time_timer_set;                /* time when physical timer was set */

TIME time_now();
void timers_update(TIME time);

struct timer *timer_declare(TIME time, void *event)
{
    struct timer *t;

    disable_interrupts();

    for (t = timers; t < &timers[MAX_TIMERS]; t++) {
        if (!t->inuse) break;
    }

    /* out of timers? */
    if (t == &timers[MAX_TIMERS]) {
        enable_interrupts();
        return NULL;
    }

    /* install new timer */
    t->event = event;
    t->time = time;
    if (!timer_next) {
        /* no timers set at all, so this is shortest */
        time_timer_set = time_now();
        start_physical_timer((timer_next = t)->time);
    } else if ((time + time_now()) < (timer_next->time + time_timer_set)) {
        /* new timer is shorter than current one */
        timers_update(time_now() - time_timer_set);
        time_timer_set = time_now();
        start_physical_timer((timer_next = t)->time);
    } else {
        /* new timer is longer than current one */
    }
    
    t->inuse = TRUE;
    enable_interrupts();
    return t;
}

void timer_undeclare(struct timer *t)
{
    disable_interrupts();
    if (!t->inuse) {
        enable_interrupts();
        return;
    }

    t->inuse = FALSE;

    /* check if we were waiting on this one */
    if (t == timer_next) {
        timers_update(time_now() - time_timer_set);
        if (timer_next) {
            start_physical_timer(timer_next->time);
            time_timer_set = time_now();
        }
    }
    enable_interrupts();
}

/* substract time from all timers, enabling any that run out along the way */
void timers_update(TIME time) {
    static struct timer timer_last = {
        FALSE,
        VERY_LONG_TIME,
        NULL,
    };

    struct timer *t;

    timer_next = &timer_last;
    
    for (t = timers; t < &timers[MAX_TIMERS]; t++) {
        if (t->inuse) {
            if (time < t->time) {
                t->time -= time;
                if (t->time < timer_next->time)
                    timer_next = t;
            } else {
                t->event();
                t->inuse = 0;
            }
        }
    }

    /* reset timer_next if no timers found */
    if (!timer_next->inuse) timer_next = NULL;
}

void timer_interrupt_handler() {
    timers_update(time_now() - time_timer_set);

    /* start physical timer for next shortest time if one exists */
    if (timer_next) {
        time_timer_set = time_now();
        start_physical_timer(timer_next->time);
    }
}

TIME time_now()
{
    TIME left;
    
    if (timer_next == NULL)
        return 0;
    else
    {
        left = alarm(0);
        alarm(left);
        return time_timer_set + timer_next->time - left;
    }
}

void signal_alarm(int signo)
{
    timer_interrupt_handler();
}

void disable_interrupts()
{
    sigset_t mask;

    sigemptyset(&mask);
    sigaddset(&mask, SIGALRM);

    sigprocmask(SIG_BLOCK, &mask, &save_mask);
}

void enable_interrupts()
{
    sigprocmask(SIG_SETMASK, &save_mask, NULL);
}

void start_physical_timer(TIME time)
{
    alarm(time);
}

void func(void)
{
    printf("time up!\n");
}

int main(int argc, char const* argv[])
{
    timers_init();

    timer_declare(3, func);
    timer_declare(3, func);
    timer_declare(5, func);
    timer_declare(1, func);
    
    for (;;)
        pause();

    return 0;
}
